//
//  HACKLineNode.m
//  HStockCharts
//
//  Created by Hotacool on 2018/9/20.
//

#import "HACKLineNode.h"
#import "HACBrushCandle.h"
#import "HACGradientWave.h"
#import "HACLineChartDataSet.h"
#import "HACPointEntry.h"
#import "HACPainter.h"

@interface HACKLineNode ()
/** k线数据 */
@property (nonatomic, copy) NSArray<HACKLineEntry*> *kLineData;
@property (nonatomic, strong) NSMutableDictionary <NSNumber*, NSArray<HACPointEntry*>*> *lineDicData;
/** 画线点数据 */
@property (nonatomic, strong) NSMutableArray *lineDrawingData;
/** 画柱形图数据 */
@property (nonatomic, strong) NSMutableArray *rectDrawingData;
/** KLine画线点数据，缩放到min scale，K线蜡烛图变成线图 */
@property (nonatomic, strong) NSMutableArray *klineDrawingData;
@property (nonatomic, strong) NSMutableDictionary <NSNumber*, NSArray<HACPointEntry*>*> *klineDicData;
@property (nonatomic, assign) CGPoint highPoint;
@property (nonatomic, assign) NSInteger highIndex;
@property (nonatomic, assign) CGPoint lowPoint;
@property (nonatomic, assign) NSInteger lowIndex;
@property (nonatomic, strong) NSMutableArray<HACPointEntry*> *featurePoints;
/** 画笔 */
@property (nonatomic, strong) HACBrushCandle *candleBrush;
@property (nonatomic, strong) HACGradientWave *lineBrush;
@property (nonatomic, strong) NSMutableArray<HACTextPainter *>* textPainters;
@property (nonatomic, strong) NSMutableArray<HACPathPainter *>* directPainters;
@property (nonatomic, strong) NSMutableArray<CALayer *>* featurePainters;
@end

@implementation HACKLineNode

- (void)setUp {
    [super setUp];
    self.pointRect = CGRectZero;
    self.singleWidth = 1.0;
    self.isSoildFill = YES;// 默认实心
    _candleBrush = [[HACBrushCandle alloc] initWithDataProvider:self];
    _lineBrush = [[HACGradientWave alloc] initWithDataProvider:self];
    
    _highPoint = CGPointMake(0, FLT_MIN);
    _lowPoint = CGPointMake(0, FLT_MAX);
    _featurePoints = [NSMutableArray array];
    _featurePainters = [NSMutableArray array];
    [self createHighLowPointLabel];
}

- (void)createHighLowPointLabel{
    _textPainters = [NSMutableArray arrayWithCapacity:2];
    _directPainters = [NSMutableArray arrayWithCapacity:2];
    HACTextPainter *tp;
    HACPathPainter *pp;
    tp = [HACTextPainter painterLayer];
    tp.zPosition = 999;
    tp.anchorPoint = CGPointMake(0, 0);
    tp.foregroundColor = [UIColor blackColor].CGColor;
    tp.font = (__bridge CFTypeRef _Nullable)([UIFont systemFontOfSize:12]);
    tp.fontSize = 12;
    tp.contentsScale = [UIScreen mainScreen].scale;;
    pp = [HACPathPainter painterLayer];
    pp.zPosition = 999;
    pp.strokeColor = [UIColor blackColor].CGColor;
    pp.lineWidth = 1;
    [_textPainters addObject:tp];
    [_directPainters addObject:pp];
    tp = [HACTextPainter painterLayer];
    tp.zPosition = 999;
    tp.anchorPoint = CGPointMake(0, 0);
    tp.foregroundColor = [UIColor blackColor].CGColor;
    tp.font = (__bridge CFTypeRef _Nullable)([UIFont systemFontOfSize:12]);
    tp.fontSize = 12;
    tp.contentsScale = [UIScreen mainScreen].scale;;
    pp = [HACPathPainter painterLayer];
    pp.zPosition = 999;
    pp.strokeColor = [UIColor blackColor].CGColor;
    pp.lineWidth = 1;
    [_textPainters addObject:tp];
    [_directPainters addObject:pp];
}

- (void)removeNode {
    [super removeNode];
    [self.candleBrush removeBrush];
    [self.lineBrush removeBrush];
}

- (void)setData:(HACChartData *)data {
    if (!data&&data.dataSets.count==2) {
        return;
    }
    //k线数据
    _kLineData = (NSArray<HACKLineEntry*>*)[data dataSetWithLabel:HACKLineViewDataLabel].values;
    HACChartDataSet *tmp = [data dataSetWithLabel:HACBasicDataLabel];
    self.basicInfoData = tmp?(HACBasicInfoEntry*)tmp.values.firstObject:nil;
    if (self.isMinScale) {
        [self updateMinScaleDate];
    } else {
        _lineDicData = [NSMutableDictionary dictionary];
        _lineDrawingData = [NSMutableArray array];
        int index = 0, maxCount = 0;
        for (HACLineChartDataSet *dataSet in data.dataSets) {
            if ([dataSet isKindOfClass:[HACLineChartDataSet class]]) {
                HACLineComponents *lineItem = [[HACLineComponents alloc] init];
                lineItem.isCurve = dataSet.cubicEnabled;
                lineItem.lineColor = dataSet.lineColor;
                lineItem.fillColor = dataSet.fillColor;
                lineItem.isGradient = dataSet.gradientEnabled;
                lineItem.index = index;
                lineItem.lineBolder = dataSet.lineWidth>1?dataSet.lineWidth:1;
                [_lineDrawingData addObject:lineItem];
                NSArray *points = dataSet.values;
                if (points) {
                    [_lineDicData setObject:points forKey:@(index)];
                }
                int tmpMax = (int)(points?points.count:0);
                if (tmpMax > maxCount) {
                    maxCount = tmpMax;
                }
                index++;
            }
        }
    }
    
    self.startIndex = MAX(0, self.actualCount - self.visualCount);
    self.endIndex = MAX(self.visualCount, self.actualCount - 1);
    [super setData:data];
}

- (void)clearNode {
    [super clearNode];
    _kLineData = nil;
    _lineDicData = nil;
    _klineDrawingData = nil;
    _rectDrawingData = nil;
    [self.candleBrush removeBrush];
    [self.lineBrush removeBrush];
    [self cleanHighLowPoint];
}

- (void)setIsMinScale:(BOOL)isMinScale {
    _isMinScale = isMinScale;
    if (isMinScale) {
        [self updateMinScaleDate];
    }
}

- (void)updateMinScaleDate {
    if (!_klineDrawingData) {
        _klineDrawingData = [NSMutableArray array];
        _klineDicData = [NSMutableDictionary dictionary];
    } else {
        [_klineDrawingData removeAllObjects];
        [_klineDicData removeAllObjects];
    }
    HACLineComponents *lineItem = [[HACLineComponents alloc] init];
    lineItem.isCurve = YES;
    lineItem.lineColor = RGB_HEX(kMLineColor);
    lineItem.index = 0;
    lineItem.lineBolder = 1;
    [_klineDrawingData addObject:lineItem];
    NSMutableArray *points = [NSMutableArray array];
    [self.kLineData enumerateObjectsUsingBlock:^(HACKLineEntry * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        HACPointEntry *point = [[HACPointEntry alloc] init];
        point.y = obj.closePrice;
        point.x = idx;
        [points addObject:point];
    }];
    [_klineDicData setObject:points forKey:@(0)];
}

- (CGFloat)maxContentWidth {
    return MAX(self.actualCount * self.singleWidth , [super maxContentWidth]);
}

- (void)updateFrame {
    self.pointRect = self.relativeFrame;
    self.singleWidth = FormatValueWithRemain(self.relativeFrame.size.width / (self.visualCount), 3);// 可见数即visualCount
}

- (void)setNeedsDisplay {
    [super setNeedsDisplay];
    if (self.hostLayer) {
        [self.candleBrush drawInLayer:self.hostLayer inRect:self.relativeFrame];
        [self.lineBrush drawInLayer:self.hostLayer inRect:self.relativeFrame];
        [self drawHighLowPointInLayer:self.hostLayer inRect:self.relativeFrame];
        [self drawFeaturePointInLayer:self.hostLayer inRect:self.relativeFrame];
    }
}

- (void)cleanHighLowPoint {
    [@[self.textPainters, self.directPainters, self.featurePainters] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj enumerateObjectsUsingBlock:^(HACTextPainter * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            [obj removeFromSuperlayer];
        }];
    }];
}

- (void)drawFeaturePointInLayer:(CALayer*)layer inRect:(CGRect)rect {
    [self.featurePainters enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj removeFromSuperlayer];
    }];
    DISABLE_ANIMATION_BEIGIN
    for (HACPointEntry *fp in self.featurePoints) {
        CALayer *haloLayer = [CALayer layer];
        haloLayer.contentsScale = [UIScreen mainScreen].scale;
        haloLayer.cornerRadius = 10;
        haloLayer.bounds = CGRectMake(0, 0, 8, 8);
        haloLayer.backgroundColor = [RGB_HEX(0xFFA02C) CGColor];
        haloLayer.position = CGPointMake(fp.x, fp.y);
        [self.hostLayer addSublayer:haloLayer];
        [self.featurePainters addObject:haloLayer];
    }
    DISABLE_ANIMATION_END
}

- (void)drawHighLowPointInLayer:(CALayer*)layer inRect:(CGRect)rect {
    if (self.isMinScale) {//clear
        [self cleanHighLowPoint];
        return;
    }
    void(^drawPoint)(CGPoint point, NSInteger index, double price, HACTextPainter *tp, HACPathPainter *pp) = ^(CGPoint point, NSInteger index, double price, HACTextPainter *tp, HACPathPainter *pp){
        if ((index >= self.kLineData.count) || !tp || !pp) {
            return;
        }
        NSString *tstr = [NSString stringWithFormat:@"%.2f", price];
        CGSize size = [tstr boundingRectWithSize:CGSizeMake(rect.size.width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:12]} context:nil].size;
        BOOL isRight = YES, isUp = YES;
        if (point.x + 50 > CGRectGetMaxX(rect)) {
            isRight = NO;
        }
        if (point.y - size.height <= 0) {
            isUp = NO;
        }
        UIBezierPath * path;CGPoint lp, hp;
        lp = CGPointMake(point.x + 20*(isRight?1:-1), point.y+(isUp?0:1));
        hp = CGPointMake(point.x, point.y+(isUp?0:1));
        path = [UIBezierPath bezierPath];
        [path moveToPoint:hp];
        [path addLineToPoint:lp];
        pp.path = path.CGPath;
        //    [pp paintInLayer:layer];
        [self.hostLayer addSublayer:pp];
        tp.string = tstr;
        tp.bounds = (CGRect){{0,0},size};
        tp.position = CGPointMake(point.x + (isRight?20:-20-size.width), point.y+size.height*(isUp?-1:0));
        [self.hostLayer addSublayer:tp];
    };
    if (self.highIndex < self.kLineData.count && self.lowIndex < self.kLineData.count) {
        DISABLE_ANIMATION_BEIGIN
        drawPoint(self.highPoint, self.highIndex, self.kLineData[self.highIndex].minPrice, self.textPainters[0], self.directPainters[0]);
        drawPoint(self.lowPoint, self.lowIndex, self.kLineData[self.lowIndex].maxPrice, self.textPainters[1], self.directPainters[1]);
        DISABLE_ANIMATION_END
    }
}

- (NSInteger)actualCount {
    return _kLineData?_kLineData.count:0;
}

#pragma mark - provider
- (NSArray *)rectDatasInRect:(CGRect)rect {
    if (self.actualCount == 0 || self.isMinScale) {
        return nil;
    }
    self.highPoint = CGPointMake(0, FLT_MIN);
    self.lowPoint = CGPointMake(0, FLT_MAX);
    [self.featurePoints removeAllObjects];
    self.highIndex = 0;
    self.lowIndex = 0;
    self.rectDrawingData = [NSMutableArray array];
    CGFloat x, open, close, max, min;
    
    HACKLineEntry *pointData, *lastData;
    for (NSInteger i = self.startIndex; i <= self.endIndex && i < self.actualCount; i ++) {
        pointData = self.kLineData[i];
        HACRectComponents *rectItem = [[HACRectComponents alloc] init];
        rectItem.valueType = [self calculatorValueType:pointData lastData:lastData];
        rectItem.lineColor = [self colorWithValueType:rectItem.valueType];
        rectItem.isSoildFill = self.isSoildFill; /** 是否实心 */
        //偏移，转化到0~visualCount范围中
        NSInteger idx = [self relativeIdxFromIndex:i];
        x = [self xPointForIndex:idx inRect:self.pointRect];
        open = [self calculatorYForValue:pointData.openPrice min:self.minValue max:self.maxValue inRect:self.pointRect];
        close = [self calculatorYForValue:pointData.closePrice min:self.minValue max:self.maxValue inRect:self.pointRect];
        max = [self calculatorYForValue:pointData.maxPrice min:self.minValue max:self.maxValue inRect:self.pointRect];
        min = [self calculatorYForValue:pointData.minPrice min:self.minValue max:self.maxValue inRect:self.pointRect];
        rectItem.pillarRect = CGRectMake(x + self.itemsSpace/2, MIN(open, close), self.singleWidth - self.itemsSpace, MAX(1, ABS(open - close)));// 保证高度至少为1
        rectItem.center = CGRectGetMidX(rectItem.pillarRect);
        
        CGLine upLine, downLine;
        if (rectItem.valueType == DrawingUpValue) {
            upLine = CGLineMake(rectItem.center, max, rectItem.center, close);
            downLine = CGLineMake(rectItem.center, min, rectItem.center, open);
        } else {
            upLine = CGLineMake(rectItem.center, max, rectItem.center, open);
            downLine = CGLineMake(rectItem.center, min, rectItem.center, close);
        }
        
        rectItem.line1 = upLine;
        rectItem.line2 = downLine;
        
        [self.rectDrawingData addObject:rectItem];
        //coordinate revert
        if (self.highPoint.y <= min) {
            self.highPoint = CGPointMake(rectItem.center, min);
            self.highIndex = i;
        }
        if (self.lowPoint.y >= max) {
            self.lowPoint = CGPointMake(rectItem.center, max);
            self.lowIndex = i;
        }
        if (pointData.remainder1 == 1) {
            HACPointEntry *fp = [[HACPointEntry alloc] initWithX:rectItem.center y:self.pointRect.size.height-4];
            [self.featurePoints addObject:fp];
        }
    }
    
    return self.rectDrawingData;
}

- (NSArray *)lineDatasInRect:(CGRect)rect {
    if (!self.data) {
        return nil;
    }
    
    NSMutableArray *drawingData;
    NSMutableDictionary *lineDicData;
    if (self.isMinScale) {
        drawingData = self.klineDrawingData;
        lineDicData = self.klineDicData;
    } else {
        drawingData = self.lineDrawingData;
        lineDicData = self.lineDicData;
    }
    
    int index = 0;
    for (HACLineComponents *lineItem in drawingData) {
        NSArray *points = lineDicData[@(index)];
        if (!points) {
            continue;
        }
        lineItem.pointCount = (self.endIndex - self.startIndex) + 1;
        NSMutableArray *pointsArray = [NSMutableArray array];
        
        for (NSInteger i = self.startIndex; i <= self.endIndex && i < self.actualCount; i++) {
            if (i >= points.count) {
                break;
            }
            id pointData = points[i];
            CGFloat x = 0.0, y = 0.0;
            NSInteger idx = [self relativeIdxFromIndex:i];
            x = [self xPointForIndex:idx inRect:self.pointRect];
            if (self.singleWidth) {//偏移1/2singleWidth,点居中
                x += self.singleWidth / 2;
            }
            CGFloat value = [self lineValueForItem:pointData lineIndex:lineItem.index];
            y = [self calculatorYForValue:value min:self.minValue max:self.maxValue inRect:self.pointRect];
            [pointsArray addObject:NSStringFromCGPoint(CGPointMake(x, y))];
        }
        lineItem.pointArray = pointsArray;
        index++;
    }
    return drawingData;
}

- (DrawingValueType)calculatorValueType:(HACKLineEntry *)currentData lastData:(HACKLineEntry *)prePointData {
    if (currentData.closePrice > currentData.openPrice)
        return DrawingUpValue;
    else if (currentData.closePrice < currentData.openPrice)
        return DrawingDownValue;
    else // 如果收盘价＝开盘价 ，根据昨收价判断
    {
        if (currentData.closePrice > prePointData.closePrice)
            return DrawingUpValue;
        else if (currentData.closePrice < prePointData.closePrice)
            return DrawingDownValue;
        else // 如果跟昨收价还相同，保持前一天一样的逻辑
            return (DrawingValueType)prePointData.closePrice >= prePointData.openPrice;
    }
    return DrawingUpValue;
}

- (CGFloat)lineValueForItem:(HACPointEntry *)data lineIndex:(NSInteger)index {
    return data.y;
}

// 计算最大，最小值
- (void)calculateLimitValue {
    CGFloat max = FLT_MIN;
    CGFloat min = FLT_MAX;
    CGFloat value;
    
    HACKLineEntry *klineData;
    for (NSInteger i = self.startIndex; i <= self.endIndex && i < self.actualCount; i++) {
        klineData = self.kLineData[i];
        
        CGFloat maxKLine = klineData.maxPrice;
        CGFloat maxIdx = maxKLine;
        if (!self.isMinScale) {
            maxIdx = [self getMaxLineValueAtIndex:i];
        }
        value = (maxIdx != kDrawingInvaildValue) ? MAX(maxKLine, maxIdx) : maxKLine;
        if (value != kDrawingInvaildValue && value > max) {
            max = value;
        }
        CGFloat mixKLine = klineData.minPrice;
        CGFloat minIdx = mixKLine;
        if (!self.isMinScale) {
            minIdx = [self getMinLineValueAtIndex:i];
        }
        value = (minIdx != kDrawingInvaildValue) ? MIN(mixKLine, minIdx) : mixKLine;
        if (value != kDrawingInvaildValue && value < min) {
            min = value;
        }
    }
    self.maxValue = (max == FLT_MIN) ? .0 : max;
    self.minValue = (min == FLT_MAX) ? .0 : min;
}

- (CGFloat)getMaxLineValueAtIndex:(int)index {
    CGFloat __block max = FLT_MIN;
    [self.lineDicData enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSArray<HACPointEntry *> * _Nonnull obj, BOOL * _Nonnull stop) {
        if (obj.count > index) {
            CGFloat value = obj[index].y;
            if (value != kDrawingInvaildValue && value > max) {
                max = value;
            }
        }
    }];
    return max;
}

- (CGFloat)getMinLineValueAtIndex:(int)index {
    CGFloat __block min = FLT_MAX;
    [self.lineDicData enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSArray<HACPointEntry *> * _Nonnull obj, BOOL * _Nonnull stop) {
        if (obj.count > index) {
            CGFloat value = obj[index].y;
            if (value != kDrawingInvaildValue && value < min) {
                min = value;
            }
        }
    }];
    return min;
}

#pragma mark -- api
- (NSUInteger)indexAtChartForCoordinate:(CGPoint)point {
    NSInteger focusIndex = [super indexAtChartForCoordinate:point];
    
    CGFloat offsetX = point.x - CGRectGetMinX(self.pointRect);
    focusIndex = offsetX / self.singleWidth;
    HACLineComponents *lineItem = self.lineDrawingData.firstObject;
    if (self.isMinScale) {
        lineItem = self.klineDrawingData.firstObject;
    }
    if (lineItem) {
        if (focusIndex > 0) {
            if (focusIndex >= lineItem.pointArray.count) {
                focusIndex = lineItem.pointArray.count - 1;
            }
        } else {
            focusIndex = 0;
        }
    }
    return focusIndex;
}

- (CGPoint)pointAtChartForIndex:(NSInteger)index {
    CGPoint point = [super pointAtChartForIndex:index];
    if (self.isMinScale) {
        HACLineComponents *lineItem = self.klineDrawingData.firstObject;
        if (lineItem.pointArray && lineItem.pointArray.count > index) {
            point = CGPointFromString(lineItem.pointArray[index]);
        }
    } else {
        if (self.rectDrawingData && self.rectDrawingData.count > index) {
            HACRectComponents *rectItem = self.rectDrawingData[index];
            point.x = rectItem.pillarRect.origin.x+rectItem.pillarRect.size.width/2;
            if (rectItem.valueType == DrawingUpValue) {
                point.y = rectItem.pillarRect.origin.y;
            } else {
                point.y = rectItem.pillarRect.origin.y + rectItem.pillarRect.size.height;
            }
        }
    }
    return point;
}

- (HACChartDataEntry*)dataAtChartForCoordinate:(CGPoint)point {
    HACChartDataEntry *indexData = [super dataAtChartForCoordinate:point];
    NSUInteger focusIndex = [self indexAtChartForCoordinate:point];
    if (self.kLineData && focusIndex < self.kLineData.count) {
        indexData = self.kLineData[focusIndex];
    }
    return indexData;
}

- (HACChartDataEntry*)dataAtChartForIndex:(NSUInteger)focusIndex {
    HACChartDataEntry *indexData = [super dataAtChartForIndex:focusIndex];
    if (self.kLineData && focusIndex < self.kLineData.count) {
        indexData = self.kLineData[focusIndex];
    }
    return indexData;
}
@end
